using System;
using System.Diagnostics;
using System.Threading;

namespace RStein.Posterous.API
{
    public static class AsyncHelper
    {

        public static void RunDelegateAsync(Delegate del, Action<Exception> excHandler, Action doneHandler, params Object[] delArgs)
        {
            if (del == null)
            {
                throw new ArgumentNullException("del");
            }

            SynchronizationContext context = SynchronizationContext.Current;
            ThreadPool.QueueUserWorkItem(o =>
                                             {
                                                                                                                                                   
                                                 try
                                                 {
                                                     Thread.CurrentThread.SetCurrentSynchronizationContext(context);
                                                     
                                                     del.Method.Invoke(del.Target, delArgs);
                                                     if (doneHandler != null)
                                                     {
                                                         context.Post(_ => doneHandler(), null);
                                                     }
                                                 }
                                                 catch (Exception e)
                                                 {
                                                     Debug.WriteLine(e);
                                                     
                                                     if (excHandler != null)
                                                     {
                                                        context.Post(_ => excHandler(e), null);    
                                                     }
                                                                                                          
                                                 }
                                                 finally
                                                 {
                                                     Thread.CurrentThread.RemoveCurrentSynchronizationContext();                                                     
                                                 }
                                             });
        }
        
        public static R RunAsync<R>(Func<AsyncCallback, Object, IAsyncResult> beginMethod, Func<IAsyncResult, R> endMethod)
        {
            #if SILVERLIGHT
            ThrowIfUiThread();
            #endif
            if (beginMethod == null)
            {
                throw new ArgumentNullException("beginMethod");
            }
            if (endMethod == null)
            {
                throw new ArgumentNullException("endMethod");
            }

            Exception ex = null;
            R result = default(R);

            var mre = new ManualResetEvent(false);
            ThreadPool.QueueUserWorkItem(obj => beginMethod(ar =>
                                                                {
                                                                    try
                                                                    {
                                                                        result = endMethod(ar);
                                                                    }
                                                                    catch (Exception e)
                                                                    {
                                                                        ex = e;
                                                                    }
                                                                    finally
                                                                    {
                                                                        mre.Set();
                                                                    }

                                                                },
                                                            null
                                                    ));
            

            mre.WaitOne();
            
            if (ex != null)
            {
                throw new PosterousException(GlobalConstants.ASYNC_EXCEPTION_TEXT, ex);
            }

            return result;
        }

        #if SILVERLIGHT
        public static void ThrowIfUiThread()
        {
            if (SilverlightSpecificHelper.IsUiThread())
            {
                throw new PosterousException(GlobalConstants.INVALID_SYNC_CALL_SILVERLIGHT_EX);
            }
        }
        #endif
    }
}